/**
 * This class / component is or should be in time the easiest way to pull and push records to and from wordpress 
 * and use them in your Flex application. You can pull in a single post, multiple posts with sorting and ranges,
 * posts by category, get a category list and much more. It also allows you to easily format each field when it 
 * is returned. It also pulls in any custom fields you have created on your post in Wordpress and adds them
 * to your results objects. Records returned from Wordpress are placed in it's dataProvider property making it
 * easy for any component to bind to it. 
 * 
 * Now you can use Flex as the front end for your blogs, image galleries, stores and more 
 * and keep the backend in Wordpress! It's as easy 1, 2, 3! Act now! Supplies are limited! 
 * Operators are waiting for your call! Call NOW! :P
 * 
 * How it works
 * It makes a call to a file called flex.php that you put in your wordpress directory on the server. The file is a xmlrpc
 * php page that has methods for connecting to wordpress. It takes the user name and password and call that you sent
 * and sends back an response in xml. This is translated down into objects with the properties and values from the posts
 * you created in wordpress. Any fields you that have formatting specified are formatted and any custom fields that 
 * are on the post you created in wordpress is added to the result object. This translation happens on every record 
 * returned. Afterwards these records are stuffed in the dataProvider array collection. If the data is a single object
 * then it is still placed in the dataProvider property as the dataProvider is untyped. You can always count on 
 * it being either a object or a array collection and this will be easy to figure out because it is based on the call
 * you make. It also puts single items and the first item into the property firstItem. Any properties bound 
 * to the dataProvider are updated and the data appears in your components. 
 * 
 * Usage:
 * Import the com.flexcapacitor.data classes. 
 *  
 * In this example we are getting the posts in the 'events' category. We are formatting the description field
 * as HTML so we can display it correctly in a textarea's htmlText property.
 * 
 * <data:DataTables id="events" 
		domain="http://www.flexcapacitor.com" 
		rpcfile="/developer/flex.php" 
		user="guest" password="guest"
		category="events" autoLoad="true" 
		showBusyCursor="true"
		result="{text1.text = event.toString() + '\n\n' + event.result;}"
		fault="text1.text= 'fault\n'+ event;"
		showPreloader="false"
		preloaderTarget="{this}">
		<data:columns>
			<data:DataField dataField="description" formatAsHTML="true"/>
		</data:columns>
		<data:dataProvider>
			<mx:Array>
				<mx:Object title="test data" eventDate="10/10/2008" description="Some text"/>
				<mx:Object title="test data" eventDate="10/10/2008" description="Some text"/>
				<mx:Object title="test data" eventDate="10/10/2008" description="Some text"/>
			</mx:Array>
		</data:dataProvider>
	</data:DataTables>
 * 
 * */

package com.drumbeatinsight.data {
	
	import com.drumbeatinsight.services.wordpress.BlogEntry;
	import com.drumbeatinsight.services.wordpress.WordPress;
	
	import flash.events.Event;
	
	import mx.core.Application;
	import mx.core.UIComponent;
	import mx.events.FlexEvent;
	import mx.managers.CursorManager;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.utils.ObjectUtil;


[DefaultBindingProperty(source="selectedItem", destination="dataProvider")]

[DefaultProperty("dataProvider")]

[DefaultTriggerEvent("change")]

	/**
	 *  Dispatched when result is returned
	 *	
	 *  @eventType mx.rpc.events.ResultEvent
	 */
	[Event(name="result", type="mx.rpc.events.ResultEvent")]
	
	/**
	 *  Dispatched when fault is returned
	 *	
	 *  @eventType mx.rpc.events.FaultEvent
	 */
	[Event(name="fault", type="mx.rpc.events.FaultEvent")]
	
	/**
	 *  Dispatched when service request is sent
	 *	
	 *  @eventType flash.events.Event
	 */
	[Event(name="send", type="flash.events.Event")]
	
	public class Data extends UIComponent {
		private var _blogURL:String;
		private var _blogid:String;
		private var _user:String;
		private var _password:String;
		
		public var GET_POST:String = "Get Post by Id";
		public var GET_POSTS:String = "Get Recent Posts";
		public var GET_POSTS_BY_CATEGORY:String = "Get Posts by Category";
		public var GET_CATEGORIES:String = "Get Categories";
		public var SUBMIT_POST:String = "Submit New Post";
		
		// call type to make
		// setting this property to "flex" will get all the posts in the category
	    [Inspectable(type="String", 
	    enumeration="Get Post by Id,Get Recent Posts,Get Posts by Category,Get Categories,Custom Call"
	    , category="General", name="Call Type", defaultValue="Get Single Post")]
		public var callType:String = "";
		
		// allows you to call custom methods in the xmlrpc file
	    [Inspectable(type="String", category="General", name="Custom Call Method", defaultValue="")]
		public var customCall:String = "";
		
		// parameters for a custom call method in the xmlrpc file
	    [Inspectable(type="String", category="General", name="Custom Call Method Parameters")]
		public var customCallParameters:Array = new Array();
		
		// the domain that wordpress is installed on, for example "http://www.flexcapacitor.com"
	    [Inspectable(type="String", category="General", name="Domain", defaultValue="http://localhost")]
		public var domain:String = "http://www.flexcapacitor.com";
		
		// path to the gateway file that you uploaded to the wordpress main directory...you did upload it right?
		// for example, "/developer/flex.php" would point to "http://www.flexcapacitor.com/developer/flex.php"
	    [Inspectable(type="String", category="General", name="XMLRPC Path", defaultValue="/flex.php")]
		public var rpcfile:String = "/developer/flex.php";
		
		// username of wordpress account that has at least subscriber rights
		// if you don't have a user account log in to wordpress and create a new user with at least subscriber priviledges
		// to add new posts you need a higher level priviledges
	    [Inspectable(type="String", category="General", name="Username", defaultValue="")]
		public var user:String = "visitor";
		
		// password of user that has at least subscriber rights
		// if you don't have a user account log in to wordpress and create a new user with at least subscriber priviledges
		// to add new posts you need a higher level priviledges
	    [Inspectable(type="String", category="General", name="Password", defaultValue="")]
		public var password:String = "visitor";
		
		// category of the posts that you want to get
		// you must set the call type to "Get Posts By Category" for this to relate to anything
		// for example, look at your wordpress site. in the categories view there are some categories, maybe one is called "flex"
		// setting this property to "flex" will get all the posts in the category
	    [Inspectable(type="String", category="General", name="Category", defaultValue="")]
		public var category:String = "";
		
		// total number of records returned
		// sometimes this may be 0 if you are calling a custom wordpress method
	    [Inspectable(type="int", category="General", name="Total Records", defaultValue="")]
		public var count:int = 0;
		
		// max amount of records to return when making a call
		// if page size is 0 then we return all records
	    [Inspectable(type="int", category="General", name="Page Size", defaultValue="0")]
		public var pageSize:int = 0;
		
		// the starting index of the range of posts to get
		// so if there are 55 posts and you want to get posts from 10 to 20 then
		// set rangeFrom to 10 and rangeTo to 20
	    [Inspectable(type="int", category="General", name="From", defaultValue="")]
		public var rangeFrom:int = 0;
		
		// the ending index of the range of posts to get
		// so if there are 55 posts and you want to get posts from 10 to 20 then
		// set rangeFrom to 10 and rangeTo to 20
	    [Inspectable(type="int", category="General", name="To", defaultValue="")]
		public var rangeTo:int = 0;
		
		// the field to sort the records by
		// for example, if you want to sort the records by title then enter "title"
	    [Inspectable(type="String", category="General", name="Sort By", defaultValue="")]
		public var sortBy:String = "";
		
		// the id of the post that you want to get
		// you must set the call type to "Get Single Post" for this to relate to anything
		// for example, look at your wordpress site. and you hover the mouse over the title of a blog post
		// you will see the id in the url
	    [Inspectable(type="Number", category="General", name="Post Id", defaultValue="0")]
		public var postId:Number = 0;
		
		// automatically makes the call and loads in the data from wordpress when the application starts up
	    [Inspectable(type="Boolean", category="General", name="Auto Load", defaultValue="true")]
		public var autoLoad:Boolean = true;
		
		// shows a busy cursor while data is being retrieved from wordpress
	    [Inspectable(type="Boolean", category="General", name="Show Busy Cursor", defaultValue="true")]
		public var showBusyCursor:Boolean = true;
		
		// includes any custom fields you created in wordpress. what? you didn't know about custom fields? 
		// when you write a post or page in wordpress scroll down the page a bit
		// you will see a custom fields category
		// expand that and enter the name of the field and the value to the far right of it
		// save your post. that field will now show up on the records in the dataprovider
	    [Inspectable(type="Boolean", category="General", name="Include Custom Fields", defaultValue="true")]
		public var includeCustomFields:Boolean = true;
		
		// reference to the preloader
		public var preloader:Object = new UIComponent();
		
		// when enabled shows a preloader on a target component or container while things load
	    [Inspectable(type="Boolean", category="General", name="Show Preloader", defaultValue="false")]
		public var showPreloader:Boolean = false;
		
		// padding to the left or right of the preloader depending on the position
	    [Inspectable(type="int", category="General", name="Preloader Padding Left", defaultValue="5")]
		public var preloaderPaddingLeft:int = 5;
	    
	    // padding from the top or bottom of the preloader depending on the position
	    [Inspectable(type="int", category="General", name="Preloader Padding Top", defaultValue="0")]
		public var preloaderPaddingTop:int = 0;
		
	    // how long a full rotation will take in milliseconds on an animated preloader
	    // preloader animation has to be enabled
	    [Inspectable(type="int", category="General", name="Preloader Animation Duration", defaultValue="800")]
		public var preloaderAnimationDuration:int = 800;
		
	    // option to include the border chrome of a component or container when positioning a preloader
	    [Inspectable(type="Boolean", category="General", name="Preloader Include Border", defaultValue="false")]
		public var preloaderIncludeBorder:Boolean = false;
		
		// option to dim the target during preloading
	    [Inspectable(type="Boolean", category="General", name="Preloader Dim the target", defaultValue="true")]
		public var preloaderDimTheTarget:Boolean = true;
		
		// option to dim the target during preloading
	    [Inspectable(type="int", category="General", name="Preloader Dim the Target Alpha", defaultValue=".4")]
		public var preloaderDimTheTargetAlpha:Number = .4;
		
		// position to place the preloader
		// valid values are
	    [Inspectable(type="String", category="General", name="Preloader Position", defaultValue="center")]
		public var preloaderPosition:String = "center";
		
		// rotates the preloader
		public var preloaderRotate:Boolean = false;
		
		// how long a full rotation is in milliseconds. default 500 ms
		[Bindable]
	    [Inspectable(type="int", category="General", name="Rotation Speed", defaultValue="500")]
	    public var preloaderRotationSpeed:int = 1000;
		
		// target component to show the preloader on
		// if left blank than the application is the target
	    [Inspectable(type="*", category="General", name="Preloader Target", defaultValue="")]
		public var preloaderTarget:*;
		
		// set this to true to not get any data from the server
		public var forceLocalTesting:Boolean = false;
		
		// populate this object with values to be submitted as a new post
		public var newPost:BlogEntry = new BlogEntry();

		// Should be an embedded image with includeInLayout set to false -?? 
		[Bindable]
	    [Inspectable(type="*", category="General", name="Icon", defaultValue="")]
		public var iconImage:* = "";
	   	
	   	// default preloader image
   		[Bindable]
   		public var preloaderImage:Class;
		
		[Bindable]
		public var entry:BlogEntry = new BlogEntry();
		
		[Bindable]
		public var entries:Array = new Array();
		
		[Bindable]
		public var categories:Array = new Array();
		
		[Bindable]
		public var dataProvider:Object;
		
		[Bindable]
		public var firstItem:Object;
		
		[Bindable]
		public var recordCount:int = 0;
		
		[Bindable]
	    [Inspectable(type="*", category="General", name="Component to Populate", defaultValue="")]
		public var populateComponent:*;
		
		public var wordpress:WordPress;
		
	    /**
	     *  @private
	     */
	    private var _columns:Array; // the array of our DataGridColumns
		
	    [Bindable("columnsChanged")]
	    [Inspectable(arrayType="mx.controls.dataGridClasses.DataGridColumn")]
	    /**
	     *  An array of DataGridColumn objects, one for each column that
	     *  can be displayed. 
	     */
	    public function get columns():Array {
	        return _columns.slice(0);
	    }
	
	    /**
	     *  @private
	     */
	    public function set columns(value:Array):void {
	        var n:int;
	        var i:int;
	
	        n = _columns.length;
	        for (i = 0; i < n; i++) {
	            //columnRendererChanged(_columns[i]);
	        }
	        
	        _columns = value.slice(0);
	        
	        n = value.length;
	        for (i = 0; i < n; i++) {
	            var column:DataField = _columns[i];
	            //column.owner = this;
	            //column.colNum = i;
	        }
			invalidateDisplayList();
	        dispatchEvent(new Event("columnsChanged"));
	    }
	    
		public function Data():void {
			
	        super();
	
	        _columns = [];
	        
		}
		
		override public function initialize():void {
			super.initialize(); // necessary???
			Application.application.addEventListener(FlexEvent.CREATION_COMPLETE, applicationCreated, false, 0, true);
			
			if (autoLoad) {
				if (forceLocalTesting==true) {
					// when are properties done being set?
					fakeResults();
				}
				else {
					send();
				}
			}
		}
		
		public function applicationCreated(event:Event):void {
			
			// handle preloader
	   		if (!preloaderTarget) {
	   			preloaderTarget = Application.application;
	   		}
	   		
		   	// show preloader or busy cursor to user while posts load 
		   	if (showPreloader) {
		   	
			   	// add custom preloader to target to show load status
			   	preloader = new UIComponent();
			   	preloader.target = preloaderTarget;
			   	preloader.icon = preloaderImage;
			   	preloader.includeBorder = preloaderIncludeBorder;
			   	preloader.position = preloaderPosition;
			   	preloader.paddingTop = preloaderPaddingTop;
			   	preloader.paddingLeft = preloaderPaddingLeft;
			   	preloader.dimTheLights = preloaderDimTheTarget;
			   	preloader.dimAlpha = preloaderDimTheTargetAlpha;
			   	preloader.animate = preloaderRotate;
			   	preloader.animationSpeed = preloaderRotationSpeed;
		   	
		   		preloader.addIcon();
		   	}
		}
		
		public function fakeResults():void {
			wordpress = new WordPress(domain, rpcfile, "", user, password);
		   	// pass in formatting options to wordpress class
		   	wordpress.formattingOptions = columns;
			wordpress.addCustomFields(dataProvider as Array, new Array());
		   	wordpress.formatDataFields(dataProvider as Array);
	   		recordCount = dataProvider.length;
	   		count = dataProvider.length;
		   	
		   	if (count > 0) {
		   		firstItem = dataProvider[0];
		   	}
		   	
		   	// dispatch an event
		   	dispatchEventMethod(new ResultEvent(ResultEvent.RESULT,false,true,dataProvider));
		}
		
		// list available methods here
		public function callMethod(method:String, args:Array):void {
			// create wordpress class
		   	wordpress = new WordPress(domain, rpcfile, "", user, password);
		   	// these all should be weak listeers so garbage collecting can clean them up later
		   	wordpress.addEventListener(ResultEvent.RESULT, onResult, false, 0, true);
		   	wordpress.addEventListener(FaultEvent.FAULT, onFault, false, 0, true);
			wordpress.callFunction(method,args);
		}
		
		public function submitPost():void {
			if (!wordpress) {
				wordpress = new WordPress(domain, rpcfile, "0", user, password);
			}
			var newPost:BlogEntry = new BlogEntry();
			newPost.title = "some title";
			newPost.description = "some crap";
			wordpress.newPost(newPost);
		}
		
	    [Event("send")]
		public function send():void {
			// create wordpress class
		   	wordpress = new WordPress(domain, rpcfile, "", user, password);
		   	// these all should be weak listeers so garbage collecting can clean them up later
		   	wordpress.addEventListener(FaultEvent.FAULT, onFault, false, 0, true);
		   	
		   	// pass in formatting options to wordpress class
		   	wordpress.formattingOptions = columns;
		   	
		   	// we should use a switch 

		   	switch(callType) {
			    case GET_POST:
			    	if (postId == 0) { trace("Data Component (" + name + "): Specify a postId other than 0") }
			   		// get single post
			   		wordpress.getPost(postId);	   		
				   	wordpress.addEventListener(ResultEvent.RESULT, onResultSingle, false, 0, true);
			        //trace(GET_POST);
			        break;
			    case GET_POSTS:
			   		// get most recent posts and include custom fields
			   		wordpress.getPosts(pageSize, includeCustomFields);
				   	wordpress.addEventListener(ResultEvent.RESULT, onResultPostsByCategory, false, 0, true);
			        //trace(GET_POSTS);
			        break;
			    case GET_POSTS_BY_CATEGORY:
			   		// get posts by category
			   		// if page size is 0 then return all records
			   		wordpress.getPostsByCategory(category, pageSize);
				   	wordpress.addEventListener(ResultEvent.RESULT, onResultPostsByCategory, false, 0, true);
			        //trace(GET_POSTS_BY_CATEGORY);
			        break;
			    case GET_CATEGORIES:
			   		// get categories
			   		wordpress.getCategories();
				   	wordpress.addEventListener(ResultEvent.RESULT, onCategoriesResult, false, 0, true);
			        //trace(GET_CATEGORIES);
			        break;
			    default:
			        trace("Data Component (" + name + "): Please specify a valid value in the callType property. Currently the value is '" + callType + "'.");
			        break;
			}
		   	
		   	if (traceResultsInConsole) {
		   		wordpress.addEventListener(ResultEvent.RESULT, traceResultsHandler, false, 0, true);
		   	}
		   	
		   	if (showBusyCursor) {
		   		CursorManager.setBusyCursor();
		   	}
		   	
		   	dispatchEvent(new Event("send"));
		}
		
		public function traceResultsHandler(event:ResultEvent):void {
			
		   	if (traceResultsInConsole) {
		   		trace("Data Component: Results from '" + name + "'\n" + ObjectUtil.toString(dataProvider));
		   	}
		}
		
		// handle get post by id 
	   	public function onResult(event:ResultEvent):void {
	   		// get the result as an array of BlogEntry objects - place in array collection when binding to dataProvider
	   		//entries = wordpress.getResultAsEntries();
	   		dataProvider = (event.result) ? event.result : dataProvider;
	   		recordCount = (dataProvider.length) ? dataProvider.length : 0;
	   		count = recordCount;
	   		
		   	removeStatusIndicators();
		   	
		   	if (count > 0) {
		   		firstItem = dataProvider[0];
		   	}
		   	
		   	// dispatch an event
		   	dispatchEventMethod(event);
	   	}
	   	
	   	// show properties on the result object in the console
	   	public var traceResultsInConsole:Boolean = false;
		
		// handle get posts by category and populate fields 
	   	public function onResultPostsByCategory(event:ResultEvent):void {
	   		// get the result as an array of BlogEntry objects - place in array collection when binding to dataProvider
	   		//entries = wordpress.getResultAsEntries();
	   		dataProvider = wordpress.getResultAsEntries();
	   		recordCount = dataProvider.length;
	   		count = dataProvider.length;
	   		
		   	removeStatusIndicators();
		   	
		   	if (count > 0) {
		   		firstItem = dataProvider[0];
		   	}
		   	
		   	// dispatch an event
		   	dispatchEventMethod(event);
	   	}

		// handle get post by category call and populate fields 
	   	public function onResultSingle(event:ResultEvent):void {
	   		// get the result as an array of BlogEntry objects - place in array collection when binding to dataProvider
	   		entry = wordpress.getResultAsEntry();
	   		dataProvider = BlogEntry(entry);
	   		count = dataProvider.length;
		   	firstItem = dataProvider;

		   	removeStatusIndicators();
		   	
		   	// dispatch an event
		   	dispatchEventMethod(event);
	   	}

		// handle get categories call 
	   	public function onCategoriesResult(event:ResultEvent):void {
	   		// get the result as an array of BlogEntry objects - place in array collection when binding to dataProvider
	   		dataProvider = wordpress.getResultAsCategories();
	   		recordCount = dataProvider.length;
	   		count = dataProvider.length;

		   	removeStatusIndicators();
		   	
		   	if (count > 0) {
		   		firstItem = dataProvider[0];
		   	}
		   	
		   	// dispatch an event
		   	dispatchEventMethod(event);
	   	}
	   	
	    [Bindable("fault")]
	   	[Event("fault")]
	   	public function onFault(event:FaultEvent):void {
		   	removeStatusIndicators();
		   	
		   	// dispatch an event
		   	dispatchEvent(event);
	   	}
	   	
	   	public function removeStatusIndicators():void {
	   		
	   		if (showPreloader) {
		   		preloader.removeIcon();
		   	}
		   	
		   	if (showBusyCursor) {
		   		CursorManager.removeBusyCursor();
		   	}
	   	}
	   	
	   	[Bindable("result")]
	   	[Event("result")]
	   	public function dispatchEventMethod(event:*):void {
		   	// dispatch an event
		   	dispatchEvent(event);
	   		
	   	}
	}
}